home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 8: LINUX Games
/
Linux Cubed Series 8 - LINUX Games.iso
/
games
/
x11
/
rpg
/
crossfir.92
/
crossfir
/
crossfire-0.92.5
/
server
/
spell_util.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-07-24
|
56KB
|
1,949 lines
/*
* static char *rcsid_spells_c =
* "$Id: spell_util.c,v 1.13 1996/07/24 07:43:21 master Exp master $";
*/
/*
CrossFire, A Multiplayer game for X-windows
Copyright (C) 1994 Mark Wedel
Copyright (C) 1992 Frank Tore Johansen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
The author can be reached via e-mail to master@rahul.net
*/
#include <global.h>
#include <spells.h>
#include <object.h>
#include <errno.h>
#ifndef __CEXTRACT__
#include <sproto.h>
#endif
#ifdef SOUND_EFFECTS
#include <sounds.h>
#endif
#ifdef sequent
/* stoopid sequent includes don't do this like they should */
extern char * sys_errlist[];
extern int sys_nerr;
#endif
char *range_name[range_size] = {
"none", "bow", "magic", "wand", "rod", "scroll", "horn"
#ifdef ALLOW_SKILLS
,"steal"
#endif
};
void init_spells() {
static int init_spells_done = 0;
int i;
if (init_spells_done)
return;
init_spell_param(); /* peterm: read the spell parameter file--newspells.c */
init_spells_done = 1;
for (i = 0; i < NROFREALSPELLS; i++)
if (spells[i].archname) {
if ((spellarch[i] = find_archetype(spells[i].archname)) == NULL)
LOG(llevError,
"Spell %s needs arch %s, your archetype file is out of date.\n",
spells[i].name,spells[i].archname);
} else
spellarch[i] = (archetype *) NULL;
#ifdef DUMP_SWITCHES
if (dump_monsters == 4) {
for (i = 0; i < NROFREALSPELLS; i++) {
char *name1 = NULL, *name2 = NULL;
if (spellarch[i]) {
name1 = spellarch[i]->name;
if (spellarch[i]->clone.other_arch)
name2 = spellarch[i]->clone.other_arch->name;
}
fprintf(stderr, "%s:%s:%s\n", spells[i].name, name1,
(name2?name2:"null"));
}
exit(0);
}
#endif
}
void spell_effect(int spell_type, int x, int y, mapstruct *map) {
if (spellarch[spell_type] != (archetype *) NULL) {
object *effect = arch_to_object(spellarch[spell_type]);
effect->x = x;
effect->y = y;
insert_ob_in_map(effect, map);
}
}
spell *find_spell(int spelltype) {
if(spelltype<0||spelltype>NROFREALSPELLS)
return NULL;
return &spells[spelltype];
}
int path_level_mod(object *op, int sp) {
spell *s = find_spell(sp);
int val;
if (op->path_denied & s->path)
return -100; /* shouldn't get here, but ... */
val = ((op->path_repelled & s->path)!=0) * -5 +
((op->path_attuned & s->path)!=0) * 5;
if (op->level - val < 1)
return op->level-1;
else
return val;
}
int check_spell_known(object *op,int sp) {
int i;
for(i=0; i < (int)op->contr->nrofknownspells; i++)
if(op->contr->known_spells[i]==sp)
return 1;
return 0;
}
/*
* cast_spell():
* Fires spell "type" in direction "dir".
* If "ability" is true, the spell is the innate ability of a monster.
* (ie, don't check for blocks_magic(), and don't add AT_MAGIC to attacktype.
*/
/* Oct 95 - added cosmetic touches for MULTIPLE_GODS hack -b.t. */
int cast_spell(object *op,object *caster,int dir,int type,int ability,SpellTypeFrom item,char *stringarg) {
#ifdef MULTIPLE_GODS
char *godname;
#endif
spell *s=find_spell(type);
int success=0,bonus;
int duration=SP_PARAMETERS[type].bdur; /* get the base duration */
object casting_object, *orig_op=op;
#ifdef MULTIPLE_GODS
if(!strcmp((godname=determine_god(op)),"none")) godname="Your spirit";
#endif
if(item!=spellNormal&&caster!=op) {
reset_object(&casting_object);
(void) memcpy((void *)((char *) &casting_object +
offsetof(object,name)),
(void *)((char *) op+offsetof(object,name)),
sizeof(object)-offsetof(object, name));
/* Things not cleared by reset_object */
casting_object.next = NULL;
casting_object.prev = NULL;
casting_object.active_next = NULL;
casting_object.active_prev = NULL;
/* these should come from the source object for the spell */
casting_object.level=caster->level;
casting_object.path_attuned=caster->path_attuned;
casting_object.path_repelled=caster->path_repelled;
casting_object.path_denied=caster->path_denied;
/* the following must come from the monster/player initiating the spell*/
casting_object.type=op->type;
casting_object.env=op->env;
casting_object.inv=op->inv;
casting_object.contr=op->contr;
casting_object.facing=op->facing;
casting_object.map=op->map;
set_owner(&casting_object, op);
casting_object.x=op->x;
casting_object.y=op->y;
/* these are needed for some spells */
casting_object.stats.luck=op->stats.luck;
casting_object.stats.Wis=op->stats.Wis;
casting_object.stats.Cha=op->stats.Cha;
casting_object.stats.Int=op->stats.Int;
/* set the object */
op= &casting_object;
}
if(s==NULL) {
LOG(llevError,"Error, unknown spell: %d\n",type);
return 0;
}
if(!(QUERY_FLAG(op, FLAG_WIZ))&&
(op->type==PLAYER)&&
(op->contr->shoottype==range_magic)&&
(item!=spellPotion)&&
(!(IS_SUMMON_SPELL(type)&&op->contr->golem!=NULL)))
{
if(!spells[type].cleric&& op->stats.sp<SP_level_spellpoint_cost(op,type))
{
new_draw_info(NDI_UNIQUE, 0,op,"You don't have enough mana.");
op->contr->count_left=0;
return 0;
}
if(spells[type].cleric&&op->stats.grace<SP_level_spellpoint_cost(op,type))
{
/* it's possible for grace to go negative */
/* Fine - let grace go negative, but how negative it is should really
* put a limit on things - in the old method, chance was the same
* no matter how negative it was.
*Instead of subtracting 10 from the roll, add in grace (which is
* negative). This puts a real limit on things.
*/
if( (RANDOM()%op->stats.Wis) +op->stats.grace -
10*SP_level_spellpoint_cost(op,type)/op->stats.maxgrace >0) {
#ifdef MULTIPLE_GODS
new_draw_info_format(NDI_UNIQUE, 0,op,
"%s grants your prayer, though you are unworthy.",godname);
#else
new_draw_info(NDI_UNIQUE, 0,op,
"God grants your prayer, though you are unworthy.");
#endif
}
else
{
prayer_failure(op,op->stats.grace,SP_level_spellpoint_cost(op,type));
#ifdef MULTIPLE_GODS
new_draw_info_format(NDI_UNIQUE, 0,op,"%s ignores your prayer.",godname);
#else
new_draw_info(NDI_UNIQUE, 0,op,"God ignores your prayer.");
#endif
op->contr->count_left=0;
return 0;
}
}
}
if (op->path_denied & s->path) {
new_draw_info(NDI_UNIQUE, 0,op, "You are unable to cast that spell.");
op->contr->count_left=0;
return RANDOM()%(s->sp)+1;
}
#ifdef CASTING_TIME
if (op->casting==-1) /* begin the casting */
{
if (item == spellNormal&&!ability){
op->casting = s->time*PATH_TIME_MULT(op,s);
op->spell = s; /* so no one cast a spell and switchs to get lower
casting times!!! */
op->spelltype = type;
op->spell_state = 1;
/* put the stringarg into the object struct so that when the
spell is actually cast, it knows about the stringarg.
necessary for the invoke command spells. */
if(stringarg) {
op->spellarg = strdup_local(stringarg);
}
else op->spellarg=NULL;
return 0;
}
} else if (op->casting != 0) {
if (op->type == PLAYER )
new_draw_info(NDI_UNIQUE, 0,op,"You are casting!");
return 0;
}
#endif
/* ban removed on clerical spells in no-magic areas */
if (!ability && item != spellPotion &&
( ((!s->cleric)&&blocks_magic(op->map,op->x,op->y))||
(( s->cleric)&&blocks_cleric(op->map,op->x,op->y)))) {
if (op->type!=PLAYER)
return 0;
if(s->cleric)
#ifdef MULTIPLE_GODS
new_draw_info_format(NDI_UNIQUE, 0,op,"This ground is unholy! %s ignores you.",godname);
#else
new_draw_info(NDI_UNIQUE, 0,op,"This ground is unholy! God ignores you.");
#endif
else
switch(op->contr->shoottype) {
case range_magic:
new_draw_info(NDI_UNIQUE, 0,op,"Something blocks your spellcasting.");
break;
case range_wand:
new_draw_info(NDI_UNIQUE, 0,op,"Something blocks the magic of your wand.");
break;
case range_rod:
new_draw_info(NDI_UNIQUE, 0,op,"Something blocks the magic of your rod.");
break;
case range_horn:
new_draw_info(NDI_UNIQUE, 0,op,"Something blocks the magic of your horn.");
break;
case range_scroll:
new_draw_info(NDI_UNIQUE, 0,op,"Something blocks the magic of your scroll.");
break;
default:
break;
}
return 0;
}
if(item == spellNormal && op->type==PLAYER&&s->cleric&&
RANDOM()%100< s->level*2 - op->level + cleric_chance[op->stats.Wis]-
op->stats.luck*3) {
#ifdef SOUND_EFFECTS
play_sound_player_only(op->contr, SOUND_FUMBLE_SPELL);
#endif
new_draw_info(NDI_UNIQUE, 0,op,"You fumble the spell.");
#ifdef CASTING_TIME
op->casting = -1;
op->spell_state = 1;
#endif
if(s->sp==0) /* Shouldn't happen... */
return 0;
return RANDOM()%(SP_level_spellpoint_cost(op,type)+1)+1;
}
#ifdef SPELL_ENCUMBRANCE
if(item == spellNormal && op->type==PLAYER && (!s->cleric) ) {
int failure = (RANDOM()%200) - op->contr->encumbrance +op->level -s->level +35;
if( failure < 0) {
new_draw_info(NDI_UNIQUE, 0,op,"You bungle the spell because you have too much heavy equipment in use.");
#ifdef SPELL_FAILURE_EFFECTS
spell_failure(op,failure,SP_level_spellpoint_cost(op,type));
#endif
return RANDOM()%(SP_level_spellpoint_cost(op,type)+ 1);
}
}
#endif /*SPELL_ENCUMBRANCE*/
/*
* This is a simplification of the time it takes to cast a spell.
* In the future, the time will have to be spent before the
* spell takes effect, and the caster can possibly be disturbed.
* (maybe that should depend upon the spell cast?)
*/
#ifdef CASTING_TIME
if (item == spellNormal && !ability ){
op->casting = -1;
op->spell_state = 1;
s = op->spell; /* set s to the cast spell */
type = op->spelltype;
stringarg = op->spellarg;
}
#else
/* It seems the that the patch that added spell casting times
* increased the time value of most spells by about 10. So divide
* by 10 to get back to more normal use.
*/
op->speed_left -= (s->time*PATH_TIME_MULT(op,s) / 10) * FABS(op->speed);
#endif
switch((enum spellnrs) type) {
case SP_BULLET:
case SP_LARGE_BULLET:
success = fire_arch(op,dir,spellarch[type],type,1);
break;
case SP_HOLY_ORB:
success = fire_arch(op,dir,spellarch[type],type,0);
break;
case SP_S_FIREBALL:
case SP_M_FIREBALL:
case SP_L_FIREBALL:
case SP_HELLFIRE:
case SP_POISON_CLOUD:
case SP_M_MISSILE:
success = fire_arch(op,dir,spellarch[type],type, !ability);
break;
case SP_MASS_CONFUSION:
case SP_SHOCKWAVE:
case SP_COLOR_SPRAY:
case SP_FACE_OF_DEATH:
case SP_COUNTER_SPELL:
case SP_BURNING_HANDS:
case SP_PARALYZE:
case SP_SLOW:
case SP_ICESTORM:
case SP_FIREBREATH:
case SP_LARGE_ICESTORM:
case SP_BANISHMENT:
success = cast_cone(op,dir,duration,type,spellarch[type],!ability);
break;
case SP_HOLY_WORD:
case SP_TURN_UNDEAD:
success = cast_cone(op,dir,duration+turn_bonus[op->stats.Wis],type,
spellarch[type],0);
break;
case SP_HOLY_WRATH:
case SP_INSECT_PLAGUE:
case SP_RETRIBUTION:
success = cast_smite_spell(op,dir,type);
break;
case SP_SUNSPEAR:
case SP_FIREBOLT:
case SP_FROSTBOLT:
case SP_S_LIGHTNING:
case SP_L_LIGHTNING:
case SP_STEAMBOLT:
success = fire_bolt(op,dir,type,!ability);
break;
case SP_BOMB:
success = create_bomb(op,dir,"bomb");
break;
case SP_GOLEM:
case SP_FIRE_ELEM:
case SP_WATER_ELEM:
case SP_EARTH_ELEM:
case SP_AIR_ELEM:
success = summon_monster(op,dir,spellarch[type],type);
break;
case SP_STAFF_TO_SNAKE:
success = staff_to_snake(op,dir,spellarch[type],type);
break;
case SP_FINGER_DEATH:
success = finger_of_death(op,dir);
break;
case SP_HOLY_SERVANT:
case SP_SUMMON_AVATAR:
success = summon_avatar(op,dir,spellarch[type],type);
break;
case SP_CONSECRATE:
success = cast_consecrate(op);
break;
case SP_SUMMON_CULT:
#ifdef MULTIPLE_GODS
success = summon_cult_monsters(op,dir);
#else
success = summon_pet(op,dir,item);
#endif
break;
case SP_PET:
success = summon_pet(op,dir, item);
break;
case SP_D_DOOR:
/* dimension door needs the actual caster, because that is what is
* moved.
*/
success = dimension_door(orig_op,dir);
break;
case SP_DARKNESS:
case SP_WALL_OF_THORNS:
case SP_CHAOS_POOL:
case SP_COUNTERWALL:
case SP_FIRE_WALL:
case SP_FROST_WALL:
case SP_EARTH_WALL:
success = magic_wall(op,dir,type);
break;
case SP_MAGIC_MAPPING:
if(op->type==PLAYER) {
spell_effect(SP_MAGIC_MAPPING, op->x, op->y, op->map);
draw_map(op);
success=1;
}
break;
case SP_FEAR:
if(op->type==PLAYER)
bonus=fear_bonus[op->stats.Cha];
else
bonus=op->head==NULL?op->level/3+1:op->head->level/3+1;
success = cast_cone(op,dir,duration+bonus,SP_FEAR,spellarch[type],!ability);
break;
case SP_WOW:
success = cast_wow(op,dir,ability, item);
break;
case SP_DESTRUCTION:
success = cast_destruction(op,5+op->stats.Int,AT_MAGIC);
break;
case SP_PERCEIVE:
success = perceive_self(op);
break;
case SP_WOR:
success = cast_wor(op);
break;
case SP_INVIS:
case SP_INVIS_UNDEAD:
case SP_IMPROVED_INVIS:
success = cast_invisible(op,type);
break;
case SP_PROBE:
success = probe(op,dir);
break;
case SP_CREATE_FOOD:
success = cast_create_food(op,dir,stringarg);
break;
case SP_EARTH_DUST:
success = cast_earth2dust(op);
break;
case SP_REGENERATION:
case SP_BLESS:
case SP_CURSE:
case SP_HOLY_POSSESSION:
case SP_STRENGTH:
case SP_DEXTERITY:
case SP_CONSTITUTION:
case SP_CHARISMA:
case SP_ARMOUR:
case SP_PROT_COLD:
case SP_PROT_FIRE:
case SP_PROT_ELEC:
case SP_PROT_POISON:
case SP_PROT_SLOW:
case SP_PROT_DRAIN:
case SP_PROT_PARALYZE:
case SP_PROT_ATTACK:
case SP_PROT_MAGIC:
case SP_PROT_CONFUSE:
case SP_PROT_CANCEL:
case SP_PROT_DEPLETE:
case SP_LEVITATE:
case SP_HEROISM:
case SP_CONFUSION:
case SP_XRAY:
case SP_DARK_VISION:
success = cast_change_attr(op,dir,type);
break;
case SP_RESTORATION:
case SP_HEAL:
case SP_MINOR_HEAL:
case SP_MED_HEAL:
case SP_MAJOR_HEAL:
case SP_CURE_POISON:
case SP_CURE_CONFUSION:
case SP_CURE_BLINDNESS:
success = cast_heal(op,dir,type);
break;
case SP_REGENERATE_SPELLPOINTS:
success = cast_regenerate_spellpoints(op);
break;
case SP_SMALL_SPEEDBALL:
case SP_LARGE_SPEEDBALL:
success = cast_speedball(op,dir,type);
break;
case SP_POLYMORPH:
success = cast_polymorph(op,dir);
break;
case SP_CHARGING:
success = recharge(op);
break;
case SP_CANCELLATION:
success = fire_cancellation(op,dir,spellarch[type],!ability);
break;
case SP_ALCHEMY:
success = alchemy(op);
break;
case SP_REMOVE_CURSE:
case SP_REMOVE_DAMNATION:
success = remove_curse(op, type, item);
break;
case SP_IDENTIFY:
success = cast_identify(op);
break;
case SP_DETECT_MAGIC:
case SP_DETECT_MONSTER:
case SP_DETECT_EVIL:
case SP_DETECT_CURSE:
case SP_SHOW_INVIS:
success = cast_detection(op, type);
break;
case SP_AGGRAVATION:
aggravate_monsters(op);
success = 1;
break;
/* peterm: following spells added */
case SP_BALL_LIGHTNING:
success = fire_arch(op,dir,find_archetype("ball_lightning"),type,!ability);
break;
case SP_METEOR_SWARM: {
int n;
n=RANDOM()%3 + RANDOM()%3 + RANDOM()%3 +3 +
SP_level_strength_adjust(op, type);
success = 1;
fire_swarm(op,dir,find_archetype("meteor"),SP_METEOR,n);
break;
}
case SP_METEOR:
success = fire_arch(op,dir,find_archetype("meteor"),type,0);
break;
case SP_MYSTIC_FIST:
success = summon_monster(op,dir,spellarch[type],type);
break;
case SP_RAISE_DEAD:
case SP_RESURRECTION:
success = cast_raise_dead_spell(op,dir,type, NULL);
break;
/* mlee */
case SP_IMMUNE_COLD:
case SP_IMMUNE_FIRE:
case SP_IMMUNE_ELEC:
case SP_IMMUNE_POISON:
case SP_IMMUNE_SLOW:
case SP_IMMUNE_DRAIN:
case SP_IMMUNE_PARALYZE:
case SP_IMMUNE_ATTACK:
case SP_IMMUNE_MAGIC:
case SP_INVULNERABILITY:
case SP_PROTECTION:
case SP_HASTE:
success=cast_change_attr(op,dir,type);
break;
/* peterm, additional spells added */
case SP_BUILD_DIRECTOR:
case SP_BUILD_BWALL:
case SP_BUILD_LWALL:
case SP_BUILD_FWALL:
success=create_the_feature(op,dir,type);
break;
case SP_RUNE_FIRE:
case SP_RUNE_FROST:
case SP_RUNE_SHOCK:
case SP_RUNE_BLAST:
case SP_RUNE_DEATH:
case SP_RUNE_ANTIMAGIC:
success = write_rune(op,dir,0,op->level,s->archname);
break;
case SP_RUNE_DRAINSP:
success = write_rune(op,dir,SP_MAGIC_DRAIN,op->level,s->archname);
break;
case SP_RUNE_TRANSFER:
success= write_rune(op,dir,SP_TRANSFER,op->level,s->archname);
break;
case SP_TRANSFER:
success = cast_transfer(op,dir);
break;
case SP_MAGIC_DRAIN:
success= drain_magic(op,dir);
break;
case SP_DISPEL_RUNE:
success = dispel_rune(op,dir,0); /* 0 means no risk of detonating rune */
break;
case SP_SUMMON_EVIL_MONST:
if(op->type==PLAYER) return 0;
success = summon_hostile_monsters(op,op->stats.maxhp,op->race);
break;
case SP_REINCARNATION:
{
object * dummy;
if(stringarg==NULL) {
new_draw_info(NDI_UNIQUE, 0,op,"Reincarnate WHO?");
success=0;
break;
}
dummy = get_object();
dummy->name = add_string(stringarg);
success = cast_raise_dead_spell(op,dir,type, dummy);
free_object(dummy);
}
break;
case SP_RUNE_MAGIC:
{ int total_sp_cost, spellinrune;
spellinrune=look_up_spell_by_name(op,stringarg);
if(spellinrune!=-1) {
total_sp_cost=SP_level_spellpoint_cost(op,spellinrune)
+spells[spellinrune].sp;
if(op->stats.sp<total_sp_cost) {
new_draw_info(NDI_UNIQUE, 0,op,"Not enough spellpoints.");
#ifdef CASTING_TIME
/* free the spell arg */
if(stringarg) {free(stringarg);stringarg=NULL; };
#endif
return 0;
}
success=write_rune(op,dir,spellinrune,op->level,stringarg);
return (success ? total_sp_cost : 0);
}
#ifdef CASTING_TIME
/* free the spell arg */
if(stringarg) {free(stringarg);stringarg=NULL; };
#endif
return 0;
}
break;
case SP_RUNE_MARK:
success=write_rune(op,dir,0,-2,stringarg);
#ifdef CASTING_TIME
/* free the spell arg */
if(stringarg) {free(stringarg);stringarg=NULL; };
#endif
break;
case SP_LIGHT:
success = cast_light(op,dir);
break;
case SP_DAYLIGHT:
success = cast_daylight(op);
break;
case SP_NIGHTFALL:
success = cast_nightfall(op);
break;
case SP_FAERY_FIRE:
success = cast_faery_fire(op);
break;
case SP_CAUSE_LIGHT:
case SP_CAUSE_HEAVY:
case SP_CAUSE_MEDIUM:
case SP_CAUSE_CRITICAL:
success = fire_arch(op,dir,spellarch[type],type,1); /* don't want to OR magic */
break;
case SP_SUMMON_FOG:
success = summon_fog(op,dir,type);
break;
case SP_PACIFY:
cast_pacify(op,caster,spellarch[type],type);
success = 1;
break;
case SP_COMMAND_UNDEAD:
cast_charm_undead(op,spellarch[type],type);
success = 1;
break;
case SP_CHARM:
cast_charm(op,spellarch[type],type);
success = 1;
break;
/* huma */
case SP_CREATE_MISSILE:
success = cast_create_missile(op,dir,stringarg);
break;
}
#ifdef SOUND_EFFECTS
play_sound_map(op->map, op->x, op->y, SOUND_CAST_SPELL_0 + type);
#endif
#ifdef CASTING_TIME
/* free the spell arg */
if(stringarg) {free(stringarg);stringarg=NULL; };
#endif
#ifdef SPELLPOINT_LEVEL_DEPEND
return success?SP_level_spellpoint_cost(op,type):0;
#else
return success?(s->sp*PATH_SP_MULT(op,s)):0;
#endif
}
int cast_create_obj(object *op,object *new_op, int dir)
{
if(dir && blocked(op->map,op->x+freearr_x[dir],op->y+freearr_y[dir])) {
new_draw_info(NDI_UNIQUE, 0,op,"Something is in the way.");
new_draw_info(NDI_UNIQUE, 0,op,"You cast it at your feet.");
dir = 0;
}
new_op->x=op->x+freearr_x[dir];
new_op->y=op->y+freearr_y[dir];
insert_ob_in_map(new_op,op->map);
return dir;
}
int summon_monster(object *op,int dir,archetype *at,int spellnum) {
object *tmp;
if(op->type==PLAYER)
if(op->contr->golem!=NULL&&!QUERY_FLAG(op->contr->golem,FLAG_FREED)) {
control_golem(op->contr->golem,dir);
return 0;
}
if(!dir)
dir=find_free_spot(NULL,op->map,op->x,op->y,1,9);
if(blocked(op->map,op->x+freearr_x[dir],op->y+freearr_y[dir])) {
new_draw_info(NDI_UNIQUE, 0,op,"There is something in the way.");
if(op->type==PLAYER)
op->contr->count_left=0;
return 0;
}
tmp=arch_to_object(at);
if(op->type==PLAYER) {
CLEAR_FLAG(tmp, FLAG_MONSTER);
tmp->stats.exp=0;
add_friendly_object(tmp);
tmp->type=GOLEM;
/* Don't see any point in setting this when monsters summon monsters: */
set_owner(tmp,op);
op->contr->golem=tmp;
/* give the player control of the golem */
op->contr->shoottype=range_scroll;
} else {
if(QUERY_FLAG(op, FLAG_FRIENDLY)) {
object *owner = get_owner(op);
if(owner != NULL) /* For now, we transfer ownership */
set_owner(tmp,owner);
tmp->move_type = PETMOVE;
add_friendly_object(tmp);
SET_FLAG(tmp, FLAG_FRIENDLY);
}
SET_FLAG(tmp, FLAG_MONSTER);
}
/* This sets the level dependencies on dam and hp for monsters */
tmp->stats.hp = SP_PARAMETERS[spellnum].bdur +
10 * SP_level_strength_adjust(op,spellnum);
tmp->stats.dam= SP_PARAMETERS[spellnum].bdam +
2* SP_level_dam_adjust(op,spellnum);
if(tmp->stats.dam<0) tmp->stats.dam=127; /*seen this go negative!*/
/* make experience increase in proportion to the strength of the summoned creature. */
tmp->stats.exp *= SP_level_spellpoint_cost(op,spellnum)/spells[spellnum].sp;
tmp->speed_left= -1;
tmp->x=op->x+freearr_x[dir],tmp->y=op->y+freearr_y[dir];
tmp->direction=dir;
insert_ob_in_map(tmp,op->map);
return 1;
}
int ok_to_put_more(mapstruct *m,int x,int y,object *op,int immune_stop) {
object *tmp,*head;
for(tmp=get_map_ob(m,x,y);tmp!=NULL;tmp=tmp->above) {
head=tmp->head==NULL?tmp:tmp->head;
if((QUERY_FLAG(op, FLAG_ALIVE) && head->immune & immune_stop) ||
(head->stats.maxhp == op->stats.maxhp && head->type == op->type))
return 0;
}
return 1;
}
int fire_bolt(object *op,int dir,int type,int magic) {
object *tmp=NULL;
if (!spellarch[type])
return 0;
tmp=arch_to_object(spellarch[type]);
if(tmp==NULL)
return 0;
/* peterm: level dependency for bolts */
tmp->stats.dam = SP_PARAMETERS[type].bdam + SP_level_dam_adjust(op,type);
tmp->stats.hp = SP_PARAMETERS[type].bdur + SP_level_strength_adjust(op,type);
if(magic)
tmp->attacktype|=AT_MAGIC;
tmp->x=op->x,tmp->y=op->y;
tmp->direction=dir;
if(QUERY_FLAG(tmp, FLAG_IS_TURNABLE))
tmp->face=&new_faces[tmp->arch->faces[dir]];
set_owner(tmp,op);
#if 0
if(op->type==PLAYER)
tmp->stats.wc=5+(op->contr->shootstrength-5)/5,
tmp->stats.exp=(op->contr->shootstrength-5)/3+12,
tmp->stats.hp=8+(op->contr->shootstrength-5)/8;
#endif
tmp->x+=DIRX(tmp),tmp->y+=DIRY(tmp);
if(wall(op->map,tmp->x,tmp->y)) {
if(!QUERY_FLAG(tmp, FLAG_REFLECTING)) {
free_object(tmp);
return 0;
}
tmp->x=op->x,tmp->y=op->y;
tmp->direction=absdir(tmp->direction+4);
}
insert_ob_in_map(tmp,op->map);
move_bolt(tmp);
return 1;
}
/* peterm added a type field to fire_arch. Needed it for making
fireball etall level dependent.
Later added a ball-lightning firing routine.
*/
int fire_arch(object *op,int dir,archetype *at, int type, int magic) {
object *tmp, *env;
if(at==NULL)
return 0;
for(env=op;env->env!=NULL;env=env->env);
if (env->map == NULL)
return 0;
tmp=arch_to_object(at);
if(tmp==NULL)
return 0;
tmp->stats.sp=type;
tmp->stats.dam=SP_PARAMETERS[type].bdam+SP_level_dam_adjust(op,type);
tmp->stats.hp=SP_PARAMETERS[type].bdur+SP_level_strength_adjust(op,type);
tmp->x=op->x,tmp->y=op->y;
tmp->direction=dir;
#ifdef MULTIPLE_GODS /* needed for AT_HOLYWORD,AT_GODPOWER stuff */
if(tmp->attacktype&AT_HOLYWORD||tmp->attacktype&AT_GODPOWER)
if(!tailor_god_spell(tmp,op,get_god(op))) return 0;
#endif
if(magic)
tmp->attacktype|=AT_MAGIC;
if(QUERY_FLAG(tmp, FLAG_IS_TURNABLE))
tmp->face=&new_faces[tmp->arch->faces[dir]];
#if 0
if(op->type==PLAYER)
tmp->stats.hp=(op->contr->shootstrength-10)/10+10;
#endif
set_owner(tmp,op);
if(op->type==PLAYER)
D_LOCK(op);
insert_ob_in_map(tmp,op->map);
/* object was used when it was inserted into the map - don't do anything
* more
*/
if (QUERY_FLAG(tmp, FLAG_FREED)) return 1;
switch(type) {
case SP_M_MISSILE:
move_missile(tmp);
break;
case SP_BALL_LIGHTNING:
tmp->stats.food=SP_PARAMETERS[type].bdur +
SP_level_strength_adjust(op,type);
move_ball_lightning(tmp);
break;
default:
move_fired_arch(tmp);
}
if(op->type==PLAYER)
D_UNLOCK(op);
return 1;
}
int
cast_cone(object *op, int dir, int strength, int spell_type,archetype *spell_arch, int magic)
{
object *tmp;
int i,success=0,range_min= -1,range_max=1;
if(!dir)
range_min= -3,range_max=4,strength/=2;
for(i=range_min;i<=range_max;i++) {
int x=op->x+freearr_x[absdir(dir+i)],
y=op->y+freearr_y[absdir(dir+i)];
if(wall(op->map,x,y))
continue;
success=1;
tmp=arch_to_object(spell_arch);
set_owner(tmp,op);
/* Make face of death work? */
/* tmp->level=op->level; */
tmp->level=SK_level(op); /* need to use the cleric level, not overall value -b.t. */
tmp->x=x,tmp->y=y;
#ifdef MULTIPLE_GODS /* holy word stuff */
if((tmp->attacktype&AT_HOLYWORD)||(tmp->attacktype&AT_GODPOWER))
if(!tailor_god_spell(tmp,op,get_god(op))) return 0;
#endif
/*if (tmp->type != CONE)
tmp->type = CONE; */
if(magic)
tmp->attacktype|=AT_MAGIC; /* JWI cone attacks should be considered
magical in nature ;) */
if(dir)
tmp->stats.sp=dir;
else
tmp->stats.sp=i;
tmp->stats.hp=strength+SP_level_strength_adjust(op,spell_type);
tmp->stats.dam=SP_PARAMETERS[spell_type].bdam +
SP_level_dam_adjust(op,spell_type);
tmp->stats.maxhp=tmp->count;
SET_FLAG(tmp, FLAG_FLYING);
insert_ob_in_map(tmp,op->map);
}
return success;
}
void move_cone(object *op) {
int i;
if (QUERY_FLAG(op, FLAG_LIFESAVE)) {/* lava saves it's life, but not yours :) */
hit_map(op,0,op->attacktype);
return;
}
if(get_owner(op)==NULL) {
remove_ob(op);
free_object(op);
return;
}
/* Hit map returns 1 if it hits a monster. If it does, set
* food to 1, which will stop the cone from progressing.
*/
op->stats.food |= hit_map(op,0,op->attacktype);
if((op->stats.hp-=2)<0) {
if(op->stats.exp) {
op->speed = 0;
update_ob_speed(op);
op->stats.exp=0;
op->stats.sp=0; /* so they will join */
} else {
remove_ob(op);
free_object(op);
}
return;
}
if(op->stats.food) return;
op->stats.food=1;
for(i= -1;i<2;i++) {
int x=op->x+freearr_x[absdir(op->stats.sp+i)],
y=op->y+freearr_y[absdir(op->stats.sp+i)];
if(!wall(op->map,x,y)&&ok_to_put_more(op->map,x,y,op,op->attacktype) &&
!blocks_view(op->map,x,y)) {
object *tmp=arch_to_object(op->arch);
set_owner(tmp,op->owner);
tmp->x=x, tmp->y=y;
/* added to make face of death work,and counterspell */
/* tmp->level=op->level;*/
tmp->level=SK_level(op);
#ifdef MULTIPLE_GODS /* holy word stuff */
if(tmp->attacktype&AT_HOLYWORD||tmp->attacktype&AT_GODPOWER)
if(!tailor_god_spell(tmp,op,-1)) return;
#endif
tmp->stats.sp=op->stats.sp,tmp->stats.hp=op->stats.hp+1;
tmp->stats.maxhp=op->stats.maxhp;
tmp->attacktype=op->attacktype;
insert_ob_in_map(tmp,op->map);
}
}
}
void fire_a_ball(object *op,int dir,int strength) {
object *tmp=clone_arch(FBULLET);
if(!dir)
LOG(llevError,"Tried to fire a ball without direction.\n");
set_owner(tmp,op);
tmp->direction=dir;
tmp->x=op->x,tmp->y=op->y;
tmp->speed = 1;
update_ob_speed(tmp);
tmp->stats.hp=strength;
tmp->face=&new_faces[tmp->arch->faces[dir]];
SET_FLAG(tmp, FLAG_FLYING);
insert_ob_in_map(tmp,op->map);
move_fired_arch(tmp);
}
void explosion(object *op) {
object *tmp;
mapstruct *m=op->map; /* In case we free b */
int i;
if(--(op->stats.hp)<0) {
remove_ob(op);
free_object(op);
return;
}
if(op->above!=NULL&&op->above->type!=PLAYER) {
remove_ob(op);
insert_ob_in_map(op,op->map);
}
hit_map(op,0,op->attacktype);
if(op->stats.hp>2&&!op->value) {
op->value=1;
for(i=1;i<9;i++) {
int dx,dy;
if(wall(op->map,dx=op->x+freearr_x[i],dy=op->y+freearr_y[i]))
continue;
if(blocks_view(op->map, dx, dy))
continue;
if(ok_to_put_more(op->map,dx,dy,op,op->attacktype)) {
tmp=get_object();
copy_object(op,tmp); /* This is probably overkill on slow computers.. */
tmp->state=0;
tmp->speed_left= -0.21;
tmp->stats.hp--;
tmp->value=0;
tmp->x=dx,tmp->y=dy;
insert_ob_in_map(tmp,m);
}
}
}
}
int reflwall(mapstruct *m,int x,int y) {
object *op;
if(out_of_map(m,x,y)) return 0;
for(op=get_map_ob(m,x,y);op!=NULL;op=op->above)
if(QUERY_FLAG(op, FLAG_REFL_SPELL))
return 1;
return 0;
}
void move_bolt(object *op) {
object *tmp;
int w,r;
if(--(op->stats.hp)<0) {
remove_ob(op);
free_object(op);
return;
}
hit_map(op,0,op->attacktype);
if(!op->value&&--(op->stats.exp)>0) {
op->value=1;
if(!op->direction)
return;
/*
* The bolt stops if it hits someone who is immune to it.
*/
tmp=get_map_ob(op->map,op->x,op->y);
while(tmp!=NULL&&(!QUERY_FLAG(tmp, FLAG_ALIVE)||!(tmp->immune&op->attacktype)))
tmp=tmp->above;
if(tmp!=NULL) {
remove_ob(op);
free_object(op);
return;
}
if(blocks_view(op->map,op->x+DIRX(op),op->y+DIRY(op)))
return;
w=wall(op->map,op->x+DIRX(op),op->y+DIRY(op));
r=reflwall(op->map,op->x+DIRX(op),op->y+DIRY(op));
if(w&&!QUERY_FLAG(op, FLAG_REFLECTING))
return;
if(w||r) { /* We're about to bounce */
if(!QUERY_FLAG(op, FLAG_REFLECTING))
return;
op->value=0;
if(op->direction&1)
op->direction=absdir(op->direction+4);
else {
int left= wall(op->map,op->x+freearr_x[absdir(op->direction-1)],
op->y+freearr_y[absdir(op->direction-1)]),
right=wall(op->map,op->x+freearr_x[absdir(op->direction+1)],
op->y+freearr_y[absdir(op->direction+1)]);
if(left==right)
op->direction=absdir(op->direction+4);
else if(left)
op->direction=absdir(op->direction+2);
else if(right)
op->direction=absdir(op->direction-2);
}
update_turn_face(op); /* A bolt *must* be IS_TURNABLE */
return;
}
else { /* Create a copy of this object and put it ahead */
tmp=get_object();
copy_object(op,tmp);
tmp->speed_left= -0.1;
tmp->value=0;
tmp->stats.hp++;
tmp->x+=DIRX(tmp),tmp->y+=DIRY(tmp);
insert_ob_in_map(tmp,op->map);
if (!tmp->stats.food) {
tmp->stats.food = 1;
move_bolt(tmp);
} else
tmp->stats.food = 0;
}
}
}
void move_golem(object *op) {
if(QUERY_FLAG(op, FLAG_MONSTER))
return; /* Has already been moved */
if(get_owner(op)==NULL) {
LOG(llevDebug,"Golem without owner destructed.\n");
remove_ob(op);
free_object(op);
return;
}
if(--op->stats.hp<0) {
new_draw_info(NDI_UNIQUE, 0,op->owner,"Your golem dissolved.");
remove_friendly_object(op);
op->owner->contr->golem=NULL;
remove_ob(op);
free_object(op);
return;
}
if(!move_ob(op,op->direction)&&
!out_of_map(op->map,op->x+freearr_x[op->direction],
op->y+freearr_y[op->direction])) {
update_object(op);
hit_map(op,op->direction,AT_PHYSICAL);
}
}
void control_golem(object *op,int dir) {
op->direction=dir;
}
void move_missile(object *op) {
int i;
object *owner;
remove_ob(op);
owner = get_owner(op);
if (owner == (object *) NULL) {
free_object(op);
return;
}
op->x+=DIRX(op),op->y+=DIRY(op);
if(!op->direction||wall(op->map,op->x,op->y)||
blocks_view(op->map,op->x,op->y)) {
free_object(op);
return;
}
if(blocked(op->map,op->x,op->y)) {
hit_map(op,0,AT_MAGIC);
free_object(op);
return;
}
i=find_dir(op->map,op->x,op->y,get_owner(op));
if(i&&i!=op->direction){
op->direction=absdir(op->direction+((op->direction-i+8)%8<4?-1:1));
op->face=&new_faces[op->arch->faces[op->direction]];
}
insert_ob_in_map(op,op->map);
}
int explode_object(object *op) {
object *tmp, *victim, *owner, *env;
if(out_of_map(op->map,op->x,op->y)) /* peterm: check for out of map obj's.*/
{
return 0;
}
for(env=op;env->env!=NULL;env=env->env);
if (env->map == NULL)
return 0;
if(op->other_arch==NULL)
return 0;
tmp=arch_to_object(op->other_arch);
/* peterm: Hack added to make objects be able to both hit for damage and
then explode. */
if(op->attacktype){
for(victim=get_map_ob(op->map,op->x,op->y);victim!=NULL;victim=victim->above)
if(QUERY_FLAG(victim,FLAG_ALIVE))
break;
hit_map(op,0,op->attacktype);
/* Should hit_map also be doing this? Why call hit_player
* again? Also, make sure victim has not been killed - it
* is possible that hit_map killed the object.
*/
if(victim!=NULL && !QUERY_FLAG(victim,FLAG_FREED))
hit_player(victim,op->stats.dam,op,op->attacktype);
}
/* peterm: hack added to make fireballs and other explosions level
** dependent: mark*/
/* op->stats.sp stores the spell which made this object here. */
if(op->owner)
tmp->stats.dam += SP_level_dam_adjust(op->owner,op->stats.sp);
if(op->attacktype&AT_MAGIC)
tmp->attacktype|=AT_MAGIC;
if((owner = get_owner(op)) != (object *) NULL) {
set_owner(tmp,owner);
if(op->chosen_skill && (op->chosen_skill != tmp->chosen_skill)){
tmp->exp_obj = op->exp_obj;
tmp->chosen_skill = op->chosen_skill;
}
}
if(op->stats.hp)
tmp->stats.hp=op->stats.hp;
tmp->stats.maxhp=op->count; /* Unique ID */
tmp->x=env->x,tmp->y=env->y;
#ifdef MULTIPLE_GODS /* needed for AT_HOLYWORD stuff -b.t. */
if(tmp->attacktype&AT_HOLYWORD||tmp->attacktype&AT_GODPOWER)
if(!tailor_god_spell(tmp,op,-1)) return 0;
#endif
if (wall(env->map,env->x,env->y))
tmp->x-=DIRX(env),tmp->y-=DIRY(env);
if (out_of_map(env->map, env->x, env->y))
free_object(tmp);
else
insert_ob_in_map(tmp,env->map);
free_object(op);
return 1;
}
void check_fired_arch(object *op) {
if(blocked(op->map,op->x,op->y)) {
object *tmp;
remove_ob(op);
if(explode_object(op))
return;
for(tmp=get_map_ob(op->map,op->x,op->y);tmp!=NULL;tmp=tmp->above)
if(QUERY_FLAG(tmp, FLAG_ALIVE))
break;
if(tmp!=NULL)
op->stats.dam-=hit_player(tmp,op->stats.dam,op,op->attacktype);
if(blocked(op->map,op->x,op->y)) {
free_object(op);
return;
}
insert_ob_in_map(op,op->map);
}
}
void move_fired_arch(object *op) {
remove_ob(op);
/* peterm: added to make comet leave a trail of burnouts
it's an unadulterated hack, but the effect is cool. */
if(op->stats.sp == SP_METEOR)
{
object * tmp1=arch_to_object(find_archetype("fire_trail"));
tmp1->x = op->x; tmp1->y = op->y;
insert_ob_in_map(tmp1,op->map);
} /* end addition. */
op->x+=DIRX(op),op->y+=DIRY(op);
if(!op->direction||wall(op->map,op->x,op->y)) {
if(explode_object(op))
return;
free_object(op);
return;
}
if(reflwall(op->map,op->x,op->y)) {
op->direction=absdir(op->direction+4);
insert_ob_in_map(op,op->map);
update_turn_face(op);
return;
}
if(blocked(op->map,op->x,op->y)) {
object *tmp;
if(explode_object(op))
return;
for(tmp=get_map_ob(op->map,op->x,op->y);tmp!=NULL;tmp=tmp->above)
if(QUERY_FLAG(tmp, FLAG_ALIVE))
break;
if(tmp!=NULL) {
/* Certain items, like speedballs, have attacktype ghosthit.
* hit_player wants to remove the object after it hits the player.
* Since it is already removed, just don't make it ghosthit, and
* remove it here
*/
if (op->attacktype & AT_GHOSTHIT) {
hit_player(tmp,op->stats.dam,op,(op->attacktype & ~AT_GHOSTHIT));
free_object(op);
return;
}
else
op->stats.dam-=hit_player(tmp,op->stats.dam,op,op->attacktype);
}
if(blocked(op->map,op->x,op->y)) {
free_object(op);
return;
}
}
insert_ob_in_map(op,op->map);
}
void drain_rod_charge(object *rod) {
rod->stats.hp -= spells[rod->stats.sp].sp;
if (QUERY_FLAG(rod, FLAG_ANIMATE))
fix_rod_speed(rod);
}
void fix_rod_speed(object *rod) {
rod->speed = (FABS(rod->arch->clone.speed)*rod->stats.hp) /
(float)rod->stats.maxhp;
if (rod->speed < 0.02)
rod->speed = 0.02;
update_ob_speed(rod);
}
/* this function is commonly used to find a friendly target for
spells such as heal or protection or armour */
object *find_target_for_friendly_spell(object *op,int dir)
{ object *tmp;
if(op->type!=PLAYER&&op->type!=RUNE) {
tmp=get_owner(op);
/* If the owner does not exist, or is not a monster, than apply the spell
* to the caster.
*/
if(!tmp || !QUERY_FLAG(tmp,FLAG_MONSTER)) tmp=op;
}
else
for(tmp=get_map_ob(op->map,op->x+freearr_x[dir],op->y+freearr_y[dir]);
tmp!=NULL;
tmp=tmp->above)
if(tmp->type==PLAYER)
break;
if(tmp==NULL) /* didn't find a player there, look in current square for a player */
for(tmp=get_map_ob(op->map,op->x,op->y);tmp!=NULL;tmp=tmp->above)
if(tmp->type==PLAYER)
break;
return tmp;
}
/* peterm: ball lightning mover. */
/* ball lightning automatically seeks out a victim, if
it sees any monsters close enough. */
void move_ball_lightning(object *op) {
int i,nx,ny,tx,ty;
remove_ob(op);
nx=op->x+DIRX(op);
ny=op->y+DIRY(op);
ty=op->y;
tx=op->x; /* the following logic makes sure that the ball
doesn't move into a wall, and makes
sure that it will move along a wall to try and
get at it's victim. */
if(!wall(op->map, nx, ny)&&!blocks_view(op->map,nx,ny)) {
tx=nx;
ty=ny;
}
else
{ i=RANDOM()%2;
if(i) {
if(!wall(op->map,op->x,ny)&&!blocks_view(op->map,op->x,ny)) ty=ny;
else if(!wall(op->map,nx,op->y)&&!blocks_view(op->map,nx,op->y)) tx=nx;
}
else {
if(!wall(op->map,nx,op->y)&&!blocks_view(op->map,nx,op->y)) tx=nx;
else if(!wall(op->map,op->x,ny)&&!blocks_view(op->map,op->x,ny)) ty=ny;
}
}
op->y=ty;
op->x=tx;
if(blocked(op->map,op->x,op->y)) hit_map(op,0,op->attacktype);
i=spell_find_dir(op->map,op->x,op->y,get_owner(op));
if(i) op->direction=i;
insert_ob_in_map(op,op->map);
}
/* raytrace:
* spell_find_dir(map, x, y, exclude) will search first the center square
* then some close squares in the given map at the given coordinates for
* live objects.
* It will not considered the object given as exlude among possible
* live objects.
* It returns the direction toward the first/closest live object if finds
* any, otherwise 0.
*/
int spell_find_dir(mapstruct *m, int x, int y, object *exclude) {
int i,max=SIZEOFFREE;
object *tmp;
if (exclude && exclude->head)
exclude = exclude->head;
for(i=(RANDOM()%8)+1;i<max;i++) {
if(wall(m, x+freearr_x[i],y+freearr_y[i]))
max=maxfree[i];
else {
tmp=get_map_ob(m,x+freearr_x[i],y+freearr_y[i]);
while(tmp!=NULL && ((tmp!=NULL&&!QUERY_FLAG(tmp,FLAG_MONSTER)&&
tmp->type!=PLAYER&&!QUERY_FLAG(tmp,FLAG_GENERATOR)) ||
(tmp == exclude || (tmp->head && tmp->head == exclude))))
tmp=tmp->above;
if(tmp!=NULL)
return freedir[i];
}
}
return 0;
}
/* peterm: */
/* peterm: the following defines the parameters for all the
spells.
bdam: base damage or hp of spell or summoned monster
bdur: base duration of spell or base range
ldam: levels you need over the min for the spell to gain one dam
ldur: levels you need over the min for the spell to gain one dur
*/
/* The following adjustments to spell strength are done in the
philosophy that the longer one knows a spell, the better one
should get at it. So the more experience levels you are above
the minimum for knowing a spell, the more effective it becomes.
most of the following adjustments are for damage only, some are
for turning undead and whatnot.
The arrays are defined in spells.h*/
/* July 1995 - I changed the next 3 functions slightly by replacing
* the casters level (op->level) with the skill level (SK_level(op))
* instead for when we have compiled with ALLOW_SKILLS - b.t.
*/
int SP_level_dam_adjust(object *op, int spell_type)
{ int adj;
#ifdef ALLOW_SKILLS
int level=SK_level(op)+path_level_mod(op, spell_type);
#else
int level=op->level+path_level_mod(op, spell_type);
#endif
adj=(level-spells[spell_type].level);
if(adj < 0) adj=0;
if(SP_PARAMETERS[spell_type].ldam)
adj/=SP_PARAMETERS[spell_type].ldam;
else adj=0;
return adj;
}
/* July 1995 - changed slightly (SK_level) for ALLOW_SKILLS - b.t. */
int SP_level_strength_adjust(object *op, int spell_type)
{ int adj;
#ifdef ALLOW_SKILLS
int level=SK_level(op)+path_level_mod(op, spell_type);
#else
int level=op->level+path_level_mod(op, spell_type);
#endif
adj= (level-spells[spell_type].level);
if(adj < 0) adj=0;
if(SP_PARAMETERS[spell_type].ldur)
adj/=SP_PARAMETERS[spell_type].ldur;
else adj=0;
return adj;
}
/* The following function scales the spellpoint cost of
a spell by it's increased effectiveness. Some of the
lower level spells become incredibly vicious at high
levels. Very cheap mass destruction. This function is
intended to keep the sp cost related to the effectiveness. */
/* July 1995 - changed slightly (SK_level) for ALLOW_SKILLS - b.t. */
int SP_level_spellpoint_cost(object *op, int spell_type)
{
spell *s=find_spell(spell_type);
# ifdef ALLOW_SKILLS
int level=SK_level(op)+path_level_mod(op, spell_type);
#else
int level=op->level+path_level_mod(op, spell_type);
#endif /* ALLOW_SKILLS */
#ifdef SPELLPOINT_LEVEL_DEPEND
int sp;
if(SP_PARAMETERS[spell_type].spl)
sp= (int) (spells[spell_type].sp *
(1.0 +
(MAX(0, (float)(level-spells[spell_type].level)/
(float)SP_PARAMETERS[spell_type].spl ))));
else sp= spells[spell_type].sp;
sp *= PATH_SP_MULT(op,s);
return MIN(sp,(spells[spell_type].sp + 50));
#else
return s->sp*PATH_SP_MULT(op,s);
#endif /* SPELLPOINT_LEVEL_DEPEND */
}
/* move_swarm_spell: peterm */
/* This is an implementation of the swarm spell. It was written for
meteor swarm, but it could be used for any swarm. A swarm spell
is a special type of object that casts swarms of other types
of spells. Which spell it casts is flexible. It fires the spells
from a set of squares surrounding the caster, in a given direction. */
void move_swarm_spell(object *op)
{ int x,y; int di;
if(!(op->stats.hp--)||get_owner(op)==NULL) {
remove_ob(op);
free_object(op);
return;
}
x=op->x; y=op->y; /* save original location of swarm object */
if(op->stats.hp) di=RANDOM()%7-3; /* get a random number of -3->3 */
else di=0; /* fire the last one from forward. */
op->x+=freearr_x[absdir(op->direction +di)];
op->y+=freearr_y[absdir(op->direction +di)];
/* for level dependence, we need to know what spell is fired. */
/* that's stored in op->stats.sp by fire_swarm */
if(!wall(op->map,op->x,op->y))
fire_arch(op,op->direction,op->other_arch,op->stats.sp,0);
op->x=x; op->y=y; /* reset original location */
}
/* fire_swarm: peterm */
/* The following routine creates a swarm of objects. It actually
sets up a specific swarm object, which then fires off all
the parts of the swarm.
Interface:
op: the caster
dir: the direction everything will be fired in
swarm_type: the archetype that will be fired
spell_type: the spell type of the archetype that's fired
n: the number to be fired.
*/
void fire_swarm(object *op,int dir,archetype *swarm_type,int spell_type,int n)
{
object *tmp;
tmp=arch_to_object(find_archetype("swarm_spell"));
tmp->x=op->x;
tmp->y=op->y;
set_owner(tmp,op); /* needed so that if swarm elements kill, caster gets xp.*/
tmp->level=op->level; /*needed later, to get level dep. right.*/
tmp->stats.sp=spell_type; /* needed later, see move_swarm_spell */
tmp->stats.hp=n; /* n in swarm*/
tmp->other_arch=swarm_type; /* the archetype of the things to be fired*/
tmp->direction=dir;
tmp->invisible=1;
insert_ob_in_map(tmp,op->map);
}
/* look_up_spell_by_name: peterm
this function attempts to find the spell spname in spells[].
if it doesn't exist, or if the op cannot cast that spname,
-1 is returned. */
int look_up_spell_by_name(object *op,char *spname) {
int numknown;
int spnum;
int plen;
int spellen;
int i;
if(spname==NULL) return -1;
if(op==NULL) numknown=NROFREALSPELLS;
else
if(QUERY_FLAG(op, FLAG_WIZ)) numknown=NROFREALSPELLS;
else numknown = op->contr->nrofknownspells;
plen=strlen(spname);
for(i=0;i<numknown;i++) {
if(op==NULL) spnum=i;
else
if(QUERY_FLAG(op,FLAG_WIZ)) spnum=i;
else spnum = op->contr->known_spells[i];
spellen=strlen(spells[spnum].name);
if(strncmp(spname,spells[spnum].name,MIN(spellen,plen)) == 0 )
return spnum;
}
return -1;
}
void put_a_monster(object *op,char *monstername) {
object *tmp;
archetype *at;
int dir;
int nx,ny;
/* find a free square nearby */
/* first we check the closest square for free squares */
if((at=find_archetype(monstername))==NULL) return;
dir=find_first_free_spot(at,op->map,op->x,op->y);
if((tmp=arch_to_object(at))!=NULL) {
nx=op->x+freearr_x[dir];
ny=op->y+freearr_y[dir];
tmp->x=nx;
tmp->y=ny;
tmp->map = op->map;
insert_ob_in_map(tmp,op->map);
/* thought it'd be cool to insert a burnout, too.*/
tmp=get_archetype("burnout");
tmp->map = op->map;
tmp->x=nx;
tmp->y=ny;
insert_ob_in_map(tmp,op->map);
}
}
/* Some local definitions for shuffle-attack */
int color_array[20];
#define black 0
#define white 1
#define red 2
#define light_blue 3
#define blue 4
#define light_green 5
#define green 6
#define yellow 7
#define khaki 8
struct {
int attacktype;
int face;
int fg;
int bg;
} ATTACKS[22] = {
{AT_PHYSICAL,0,0,12},
{AT_PHYSICAL,0,0,12}, /*face = explosion*/
{AT_PHYSICAL,0,0,12},
{AT_MAGIC,1,1,12},
{AT_MAGIC,1,1,12}, /* face = last-burnout */
{AT_MAGIC,1,1,12},
{AT_FIRE,2,3,12},
{AT_FIRE,2,3,12}, /* face = fire.... */
{AT_FIRE,2,3,12},
{AT_ELECTRICITY,3,11,9},
{AT_ELECTRICITY,3,11,9}, /* ball_lightning */
{AT_ELECTRICITY,3,11,9},
{AT_COLD,4,5,1},
{AT_COLD,4,5,1}, /* face=icestorm*/
{AT_COLD,4,5,1},
{AT_CONFUSION,5,0,12},
{AT_POISON,7,0,12},
{AT_POISON,7,0,12}, /* face = acid sphere. generator */
{AT_POISON,7,8,12}, /* poisoncloud face */
{AT_SLOW,8,0,12},
{AT_PARALYZE,9,11,9},
{AT_FEAR,10,0,12} };
/* shuffle_attack: peterm */
/* This routine shuffles the attack of op to one of the
ones in the list. It does this at random. It also
chooses a face appropriate to the attack that is
being committed by that square at the moment.
right now it's being used by color spray and create pool of
chaos. */
void shuffle_attack(object *op,int change_face)
{
int i;
i=RANDOM()%22;
op->attacktype|=ATTACKS[i].attacktype|AT_MAGIC;
if(change_face) {
op->face=&new_faces[op->arch->faces[ATTACKS[i].face]];
#if 0
op->face.fg=ATTACKS[i].fg;
op->face.bg=ATTACKS[i].bg;
#endif
}
}
/* the following function reads from the file 'spell_params' in
the lib dir, and resets the array in memory to reflect the values
in spell_parameters. The format in there MUST be:
spell name
spell_number bdam bdur ldam ldur
for
base damage of spell, base duration of spell, level-dependency for damage
level-dependency for duration--examples
magic bullet
0 0 0 0
large icestorm
0 1 1 1
small fireball
1 0 0 8
....
The parameters have different effects for different spells.
Please refer to the documentation.
*/
void init_spell_param()
{
FILE *spell_params;
char fname[MAX_BUF];
char spell_name[50];
char spell_attrib[50];
int bdam,bdur,ldam,ldur;
int sp;
int level;
int spellindex;
int spl; /* the spellpoint level dependency */
/* This is hokey, but this function gets called everytime.
I need these colors for shuffle-attack to work right, and
they seem to change form implementation to implemention of
crossfire. So I'm making my own array here for use in
shuffle-attack. it's global in scope to this file.*/
color_array[black]=find_color("black");
color_array[white]=find_color("white");
color_array[red]=find_color("red");
color_array[light_blue]=find_color("light_blue");
color_array[blue]=find_color("blue");
color_array[light_green]=find_color("light_green");
color_array[green]=find_color("green");
color_array[yellow]=find_color("yellow");
color_array[khaki]=find_color("khaki");
/*explosion--for physical*/
ATTACKS[0].fg=color_array[black];
ATTACKS[0].bg=color_array[khaki];
ATTACKS[1].fg=color_array[black];
ATTACKS[1].bg=color_array[khaki];
ATTACKS[2].fg=color_array[black];
ATTACKS[2].bg=color_array[khaki];
/*magic--burnout attack */
ATTACKS[3].fg=color_array[light_blue];
ATTACKS[3].bg=color_array[khaki];
ATTACKS[4].fg=color_array[light_blue];
ATTACKS[4].bg=color_array[khaki];
ATTACKS[5].fg=color_array[light_blue];
ATTACKS[5].bg=color_array[khaki];
/*fire--for fire*/
ATTACKS[6].fg=color_array[red];
ATTACKS[6].bg=color_array[khaki];
ATTACKS[7].fg=color_array[red];
ATTACKS[7].bg=color_array[khaki];
ATTACKS[8].fg=color_array[red];
ATTACKS[8].bg=color_array[khaki];
/*electricity--ball lightning face */
ATTACKS[9].fg=color_array[yellow];
ATTACKS[9].bg=color_array[khaki];
ATTACKS[10].fg=color_array[yellow];
ATTACKS[10].bg=color_array[khaki];
ATTACKS[11].fg=color_array[yellow];
ATTACKS[11].bg=color_array[khaki];
/*icestorm--for cold*/
ATTACKS[12].fg=color_array[light_blue];
ATTACKS[12].bg=color_array[white];
ATTACKS[13].fg=color_array[light_blue];
ATTACKS[13].bg=color_array[white];
ATTACKS[14].fg=color_array[light_blue];
ATTACKS[14].bg=color_array[white];
/* madness--madness*/
ATTACKS[15].fg=color_array[black];
ATTACKS[15].bg=color_array[khaki];
/* poison -- poisoncloud */
ATTACKS[16].fg=color_array[white];
ATTACKS[16].bg=color_array[light_green];
ATTACKS[17].fg=color_array[white];
ATTACKS[17].bg=color_array[light_green];
ATTACKS[18].fg=color_array[white];
ATTACKS[18].bg=color_array[light_green];
/* slow */
ATTACKS[19].fg=color_array[black];
ATTACKS[19].bg=color_array[khaki];
/* paralize -- stars */
ATTACKS[20].fg=color_array[yellow];
ATTACKS[20].bg=color_array[khaki];
/* fear */
ATTACKS[21].fg=color_array[black];
ATTACKS[21].bg=color_array[khaki];
sprintf(fname,"%s/%s",LibDir,"spell_params");
if(! (spell_params=fopen(fname,"r")))
{
perror(fname);
return;
}
while(!feof(spell_params))
{
fgets(spell_name,49,spell_params);
spellindex=look_up_spell_by_name(NULL,spell_name);
if(spellindex==-1) {
fprintf(stderr,"\nUnrecognized spell: %s",spell_name);
continue;
}
fgets(spell_attrib,49,spell_params);
sscanf(spell_attrib,"%d %d %d %d %d %d %d",&level,&sp,&bdam,&bdur,&ldam,&ldur,&spl);
spells[spellindex].sp=sp;
spells[spellindex].level=level;
SP_PARAMETERS[spellindex].bdam=bdam;
SP_PARAMETERS[spellindex].bdur=bdur;
SP_PARAMETERS[spellindex].ldam=ldam;
SP_PARAMETERS[spellindex].ldur=ldur;
SP_PARAMETERS[spellindex].spl=spl;
}
}
/* get_pointed_target() - this is used by finger of death
* and the 'smite' spells. Returns the pointer to the first
* monster in the direction which is pointed to by op. b.t.
*/
object *get_pointed_target(object *op, int dir) {
object *target;
int x,y;
if (dir==0) return NULL;
for(x=op->x+freearr_x[dir],y=op->y+freearr_y[dir]
;!out_of_map(op->map,x,y)&&!blocks_view(op->map,x,y)
&&!wall(op->map,x,y);x+=freearr_x[dir],y+=freearr_y[dir])
for(target=get_map_ob(op->map,x,y);target;target=target->above) {
if(target==NULL) break;
if(QUERY_FLAG(target,FLAG_MONSTER))
if(!blocks_magic(op->map,x,y))
return target;
else break;
}
return ((object *) NULL);
}
/* cast_smite_arch() - the priest points to a creature and causes
* a 'godly curse' to decend. I generalized this a bit so that several
* spells will be possible to use w/ this code (eg fire_arch, cast_cone).
* -b.t.
*/
int cast_smite_spell (object *op, int dir, int type) {
object *effect, *target = get_pointed_target(op,dir);
#ifdef MULTIPLE_GODS
int godnr = get_god(op);
#endif
if(!target
#ifdef MULTIPLE_GODS /* if we don't worship a god, or target a creature
* of our god, the spell will fail. */
|| godnr ==-1
||(target->title&&godnr==(get_god(target)))
||(target->race&&strstr(target->race,Gods[godnr].aligned_race))
#endif
) {
new_draw_info(NDI_UNIQUE,0,op,"Your request is unheeded.");
return 0;
}
if (spellarch[type] != (archetype *) NULL)
effect = arch_to_object(spellarch[type]);
else
return 0;
/* tailor the effect by priest level and worshipped God */
effect->level=SK_level(op);
#ifdef MULTIPLE_GODS
if(effect->attacktype&AT_HOLYWORD||effect->attacktype&AT_GODPOWER)
if(tailor_god_spell(effect,op,godnr))
new_draw_info_format(NDI_UNIQUE,0,op,
"%s answers your call!",determine_god(op));
else {
new_draw_info(NDI_UNIQUE,0,op,"Your request is ignored.");
return 0;
}
#endif
/* size of the area of destruction */
effect->stats.hp=SP_PARAMETERS[type].bdur +
SP_level_strength_adjust(op,type);
/* how much woe to inflict :) */
effect->stats.dam=SP_PARAMETERS[type].bdam +
SP_level_dam_adjust(op,type);
if(effect->stats.dam<0) effect->stats.dam = 127;
effect->stats.maxhp=effect->count; /*??*/
set_owner(effect,op);
/* ok, tell it where to be, and insert! */
effect->x=target->x;effect->y=target->y;
insert_ob_in_map(effect,op->map);
return 1;
}